package com.chen.time.timing.interpolation;

public class KeyFrames<T> {

    private KeyValues<T> keyValues;
    private KeyTimes keyTimes;
    private KeyInterpolators interpolators;

    public KeyFrames(KeyValues<T> keyValues) {
        init(keyValues, null, (Interpolator) null);
    }

    public KeyFrames(KeyValues<T> keyValues, KeyTimes keyTimes) {
        init(keyValues, keyTimes, (Interpolator) null);
    }

    public KeyFrames(KeyValues<T> keyValues, KeyTimes keyTimes, Interpolator... interpolators) {
        init(keyValues, keyTimes, interpolators);
    }

    public KeyFrames(KeyValues<T> keyValues, Interpolator... interpolators) {
        init(keyValues, null, interpolators);
    }

    private void init(KeyValues<T> keyValues, KeyTimes keyTimes, Interpolator... interpolators) {
        int numFrames = keyValues.getSize();
        // If keyTimes null, create our own
        if (keyTimes == null) {
            float keyTimesArray[] = new float[numFrames];
            float timeVal = 0.0f;
            keyTimesArray[0] = timeVal;
            for (int i = 1; i < numFrames - 1; ++i) {
                timeVal += 1.0f / (numFrames - 1);
                keyTimesArray[i] = timeVal;
            }
            keyTimesArray[numFrames - 1] = 1.0f;
            this.keyTimes = new KeyTimes(keyTimesArray);
        } else {
            this.keyTimes = keyTimes;
        }
        this.keyValues = keyValues;
        if (numFrames != this.keyTimes.getSize()) {
            throw new IllegalArgumentException("keyValues and keyTimes" + " must be of equal size");
        }
        if (interpolators != null && interpolators.length != numFrames - 1 && interpolators.length != 1) {
            throw new IllegalArgumentException("interpolators must be "
                    + "either null (implying interpolation for all intervals), "
                    + "a single interpolator (which will be used for all "
                    + "intervals), or a number of interpolators equal to "
                    + "one less than the number of times.");
        }
        this.interpolators = new KeyInterpolators(numFrames - 1, interpolators);
    }

    Class<?> getType() {
        return keyValues.getType();
    }

    KeyValues<T> getKeyValues() {
        return keyValues;
    }

    KeyTimes getKeyTimes() {
        return keyTimes;
    }

    public int getInterval(float fraction) {
        return keyTimes.getInterval(fraction);
    }

    Object getValue(float fraction) {
        int interval = getInterval(fraction);
        float t0 = keyTimes.getTime(interval);
        float t1 = keyTimes.getTime(interval + 1);
        float t = (fraction - t0) / (t1 - t0);
        float interpolatedT = interpolators.interpolate(interval, t);
        if (interpolatedT < 0f) {
            interpolatedT = 0f;
        } else if (interpolatedT > 1f) {
            interpolatedT = 1f;
        }
        return keyValues.getValue(interval, interval + 1, interpolatedT);
    }

}
